/**
 * ======================================================
 * arsstats.cpp
 * 
 * Primary plugin class
 * ======================================================
 *
 * The MIT License
 * 
 * Copyright (c) 2008 Matthew Maravillas
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * Version: $Id$
 */

#include <stdio.h>
#include "arsstats.h"
#include "events.h"
#include "util.h"

SH_DECL_HOOK3_void(IServerGameDLL, ServerActivate, SH_NOATTRIB, 0, edict_t *, int, int);
SH_DECL_HOOK1_void(IServerGameClients, ClientDisconnect, SH_NOATTRIB, 0, edict_t *);
SH_DECL_HOOK2_void(IServerGameClients, ClientPutInServer, SH_NOATTRIB, 0, edict_t *, char const *);
SH_DECL_HOOK6(IServerGameDLL, LevelInit, SH_NOATTRIB, 0, bool, char const *, char const *, char const *, char const *, bool, bool);

ArsStatsPlugin gArsStatsPlugin;

PLUGIN_EXPOSE(ArsStatsPlugin, gArsStatsPlugin);

bool ArsStatsPlugin::Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlen, bool late)
{
	PLUGIN_SAVEVARS();

    DEBUG0("Populating engine factory interfaces");

	GET_V_IFACE_CURRENT(GetEngineFactory, mEngine, IVEngineServer, INTERFACEVERSION_VENGINESERVER);
	GET_V_IFACE_CURRENT(GetEngineFactory, mGameEvents, IGameEventManager2, INTERFACEVERSION_GAMEEVENTSMANAGER2);
	GET_V_IFACE_CURRENT(GetEngineFactory, mHelpers, IServerPluginHelpers, INTERFACEVERSION_ISERVERPLUGINHELPERS);
	GET_V_IFACE_CURRENT(GetEngineFactory, mCvar, ICvar, CVAR_INTERFACE_VERSION);


    DEBUG0("Populating server factory interfaces");

	GET_V_IFACE_ANY(GetServerFactory, mServer, IServerGameDLL, INTERFACEVERSION_SERVERGAMEDLL);
	GET_V_IFACE_ANY(GetServerFactory, mGameClients, IServerGameClients, INTERFACEVERSION_SERVERGAMECLIENTS);
	GET_V_IFACE_ANY(GetServerFactory, mPlayerInfoManager, IPlayerInfoManager, INTERFACEVERSION_PLAYERINFOMANAGER);

    DEBUG0("Adding automatic hooks");

	SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientPutInServer, mGameClients, this, &ArsStatsPlugin::ClientPutInServer, true);
    SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientDisconnect, mGameClients, this, &ArsStatsPlugin::ClientDisconnect, true);
	SH_ADD_HOOK_MEMFUNC(IServerGameDLL, LevelInit, mServer, this, &ArsStatsPlugin::LevelInit, true);
	//SH_ADD_HOOK_MEMFUNC(IServerGameDLL, ServerActivate, mServer, this, &ArsStatsPlugin::ServerActivate, true);

	if (late)
	{
        DEBUG0("Loading plugin late");
        ServerActivate(NULL, 0, 0);
	}

    DEBUG0("Starting plugin");

	return true;
}

bool ArsStatsPlugin::Unload(char *error, size_t maxlen)
{
	DEBUG0("Unloading plugin");

	SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientDisconnect, mGameClients, this, &ArsStatsPlugin::ClientDisconnect, true);
	SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientPutInServer, mGameClients, this, &ArsStatsPlugin::ClientPutInServer, true);
	SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, LevelInit, mServer, this, &ArsStatsPlugin::LevelInit, true);

	DEBUG0("Hooks removed");

    if (mGameEvents) 
    {
	    mGameEvents->RemoveListener(mPlayerDisconnectListener);
	    mGameEvents->RemoveListener(mPlayerDeathListener);
	    mGameEvents->RemoveListener(mPlayerChangeClassListener);
	    mGameEvents->RemoveListener(mPlayerHurtListener);
	    //mGameEvents->RemoveListener(mPlayerCalledForMedicListener);
	    //mGameEvents->RemoveListener(mPlayerHealedMedicCallListener);
	    //mGameEvents->RemoveListener(mPlayerExtinguishedListener);
        mGameEvents->RemoveListener(mPlayerInvlunedListener);
    }

    if (mPlayerDisconnectListener) 
    {
        delete mPlayerDisconnectListener;
        mPlayerDisconnectListener = NULL;
    }
    
    if (mPlayerDeathListener) 
    {
        delete mPlayerDeathListener;
        mPlayerDeathListener = NULL;
    }

    if (mPlayerChangeClassListener) 
    {
        delete mPlayerChangeClassListener;
        mPlayerChangeClassListener = NULL;
    }

    if (mPlayerHurtListener) 
    {
        delete mPlayerHurtListener;
        mPlayerHurtListener = NULL;
    }

    //delete mPlayerCalledForMedicListener;
    //delete mPlayerHealedMedicCallListener;
    //delete mPlayerExtinguishedListener;
    
    if (mPlayerInvlunedListener) 
    {
        delete mPlayerInvlunedListener;
        mPlayerHurtListener = NULL;
    }   

	DEBUG0("Listeners removed");

    if (mStats != NULL)
    {
        delete mStats;
        mStats = NULL;
	    DEBUG0("Stats deleted");
    }

    DEBUG0("Stopping plugin");
 
	return true;
}

void ArsStatsPlugin::ServerActivate(edict_t *pEdictList, int edictCount, int clientMax) 
{
    DEBUG0("Creating Stats");

    mStats = new Stats(mServer, mEngine, mPlayerInfoManager);

    DEBUG0("Creating listeners");

	mPlayerDisconnectListener       = new LoggedListener(mStats);
	mPlayerDeathListener            = new LoggedListener(mStats);
	mPlayerChangeClassListener      = new LoggedListener(mStats);
	mPlayerHurtListener             = new PlayerHurtListener(mStats);
    //mPlayerCalledForMedicListener   = new SimpleEventListener(mStats, "%s called for a medic");
    //mPlayerHealedMedicCallListener  = new SimpleEventListener(mStats, "%s answered a medic call");
    //mPlayerExtinguishedListener     = new SimpleEventListener(mStats, "%s extinguished a burning player");
    mPlayerInvlunedListener         = new PlayerInvlunedListener(mStats);

    DEBUG0("Adding listeners");

	mGameEvents->AddListener(mPlayerDisconnectListener,         "player_disconnect",        true);
	mGameEvents->AddListener(mPlayerDeathListener,              "player_death",             true);
	mGameEvents->AddListener(mPlayerChangeClassListener,        "player_changeclass",       true);
	mGameEvents->AddListener(mPlayerHurtListener,               "player_hurt",              true);
    //mGameEvents->AddListener(mPlayerCalledForMedicListener,     "player_calledformedic",    true);
    //mGameEvents->AddListener(mPlayerHealedMedicCallListener,    "player_healedmediccall",   true);
    //mGameEvents->AddListener(mPlayerExtinguishedListener,       "player_extinguished",      true);
    mGameEvents->AddListener(mPlayerInvlunedListener,           "player_invulned",          true);

    // Random notes in a bad place:
    //  m_iScore, m_iTotalScore
    //  CWeaponMedigun::m_flChargeLevel 

    DEBUG0("Initializing connected players");

    int clientCount = g_SMAPI->GetCGlobals()->maxClients;
    
    for (int i = 1; i <= clientCount; ++i)
    {
	    edict_t* edict = mEngine->PEntityOfEntIndex(i);
    	
        if (strcmp(edict->GetClassName(), "player") == 0) {
            mStats->AddPlayer(
                edict, 
                i,
                mEngine->GetPlayerUserId(edict),
                mEngine->GetPlayerNetworkIDString(edict),
                mEngine->GetClientConVarValue(i, "name"));
        }
        DEBUG1("entity #%d", i);
    }

    DEBUG0("Plugin initialized");
}

void ArsStatsPlugin::AllPluginsLoaded()
{
    DEBUG0("All plugins loaded");
}

bool ArsStatsPlugin::Pause(char *error, size_t maxlen)
{
	return true;     
}

bool ArsStatsPlugin::Unpause(char *error, size_t maxlen)
{
	return true;
}

void ArsStatsPlugin::ClientPutInServer(edict_t *edict, char const *name)
{
    DEBUG0("Hook called: ClientPutInServer");

    int entityId = mEngine->IndexOfEdict(edict);

    mStats->AddPlayer(
        edict, 
        entityId,
        mEngine->GetPlayerUserId(edict),
        mEngine->GetPlayerNetworkIDString(edict),
        mEngine->GetClientConVarValue(entityId, "name"));
}

void ArsStatsPlugin::ClientDisconnect(edict_t *edict)
{
    DEBUG0("Hook called: ClientDisconnect");

    mStats->RemovePlayer(mEngine->IndexOfEdict(edict));
}

bool ArsStatsPlugin::LevelInit(const char *pMapName,
                                char const *pMapEntities,
								char const *pOldLevel,
								char const *pLandmarkName,
								bool loadGame,
								bool background)
{
    DEBUG0("Hook called: LevelInit");

    META_LOG(g_PLAPI, "Map started: %s", pMapName);

	return true;
}